﻿#pragma once

#include "../../src/repo/src/include/root/rpc_proxy.h"
#include "../../src/repo/src/include/root/rpc_statistics.hh"
#include "../../src/repo/src/include/root/rpc_stub.h"
#include "../argument/box_argument.hh"
#include "../box/memory_allocator.hh"
#include "../box/proc_stat.hh"
#include "../box/service_logger.hh"
#include "../command/command.hh"
#include "../config/box_config.hh"
#include "../csv/csv_reader.hh"
#include "../time/local_system_time.hh"
#include <ctime>
#include <memory>

namespace rpc {
class Proxy;
class Transport;
template <typename T, typename TransT>
std::shared_ptr<T> getService(TransT transport, bool attached, rpc::Rpc *);
template <typename T, typename TransT>
std::shared_ptr<T> getService(GlobalIndex globalIndex, TransT transport,
                              bool attached, rpc::Rpc *);
} // namespace rpc

namespace kratos {
namespace http {
class HttpBase;
}
} // namespace kratos

namespace kratos {
namespace redis {
class Redis;
}
} // namespace kratos

namespace kratos {
namespace time {
class LocalTime;
}
} // namespace kratos

namespace kratos {
namespace console {
class BoxConsole;
}
} // namespace kratos

namespace kratos {
namespace lua {
class LuaService;
}
} // namespace kratos

namespace kratos {
namespace service {

class Scheduler;
class CoroRunner;
class ServiceLogger;
class Command;
class Util;

using SchedulePtr = std::unique_ptr<kratos::service::Scheduler>;
using LoggerPtr = std::unique_ptr<kratos::service::ServiceLogger>;
using LocalTimePtr = std::unique_ptr<kratos::time::LocalTime>;
using HttpPtr = std::unique_ptr<kratos::http::HttpBase>;
using RedisPtr = std::unique_ptr<kratos::redis::Redis>;
using CommandPtr = std::unique_ptr<kratos::service::Command>;
using CsvManagerPtr = std::unique_ptr<kratos::util::CsvManager>;
using BoxConsolePtr = std::unique_ptr<kratos::console::BoxConsole>;
using LuaServicePtr = std::unique_ptr<kratos::lua::LuaService>;
using UtilPtr = std::unique_ptr<kratos::service::Util>;

/**
 * @brief 用户使用的上下文接口，用来调用ServiceBox提供的功能
 * @detail
 * 内部封装了ServiceBox的实现细节，对外提供统一的接口，这个也是用户服务内调用ServiceBox
 * 的唯一接口，在用户组件内通过getContext得到ServiceContext接口指针，服务容器
 * 保证ServiceContext在组件被卸载前都是有效的，通过这个接口服务容器提供了基础的跨平台功能，包含：
 * 1. 服务注册
 * 2. 服务发现
 * 3. 日志
 * 4. 获取容器启动参数
 * 5. 获取容器启动配置
 * 6. 获取容器配置器
 * 7. 定时器
 * 8. 服务获取
 * 9. HTTP客户端/服务器
 * 10. 内存分配器
 * 11. 外部命令处理
 * 12. Redis客户端
 * 13. 服务容器统计器
 * 14. 模块日志
 * 15. 本地时间计算
 * 16. CSV管理
 * 17. 控制台
 * 18. Lua服务
 * 19. Util工具
 */
class ServiceContext {
public:
  virtual ~ServiceContext() {}
  /**
   * \brief 注册服务.
   * 向集群内注册一个服务，注册成功后集群内任何服务容器内的服务都可以看到这个服务并
   * 可以使用这个服务，服务名是一个目录结构类似:/a/b/c/d...,页节点为实际服务
   * @param name 服务名
   * @retval true 成功
   * @retval false 失败
   */
  virtual auto register_service(const std::string &name) -> bool = 0;
  /**
   * 取消服务注册.
   * @param name 服务名
   * @retval true 成功
   * @retval false 失败
   */
  virtual auto unregister_service(const std::string &name) -> bool = 0;
  /**
   * 取得启动命令行参数
   *
   * \return BoxArgument引用
   */
  virtual auto get_argument() const -> const argument::BoxArgument & = 0;
  /**
   * 写日志, [线程安全].
   * @param log_level 日志等级 @see klogger::Logger
   * @param log_line 日志行
   */
  virtual auto write_log_line(int log_level, const std::string &log_line)
      -> void = 0;
  /**
   * 取得配置器.
   *
   * @return BoxConfig 引用
   */
  virtual auto get_config() const -> kratos::config::BoxConfig & = 0;
  /**
   * 关闭系统
   */
  virtual auto shutdown() -> void = 0;
  /**
   * 协程睡眠
   * @param ms 毫秒s
   */
  virtual auto sleep_co(std::time_t ms) -> void = 0;
  /**
   * 尝试获取服务代理,如果没有找到则立刻返回.
   * @param service_name 服务名
   * @return 代理std::shared_ptr<T>
   */
  template <typename T>
  auto try_get_proxy(const std::string &service_name) -> std::shared_ptr<T> {
    auto trans = try_get_transport(service_name);
    if (!trans) {
      return nullptr;
    }
    auto prx = rpc::getService<T>(trans, true, get_rpc());
    if (prx) {
      prx->setRpc(get_rpc());
    }
    return prx;
  }
  /**
   * 获取服务代理,等待timeout毫秒，在等待期间会阻塞调用线程.
   * @param service_name 服务名
   * @param timeout 最长等待时间, 毫秒
   * @return 代理std::shared_ptr<T>
   */
  template <typename T>
  auto get_proxy_sync(const std::string &service_name, std::time_t timeout)
      -> std::shared_ptr<T> {
    auto trans = get_transport_sync(service_name, timeout);
    if (!trans) {
      return nullptr;
    }
    auto prx = rpc::getService<T>(trans, true, get_rpc());
    if (prx) {
      prx->setRpc(get_rpc());
    }
    return prx;
  }
  /**
   * 获取服务代理，在实例建立过程中将阻塞执行执行 '协程'.
   * @param name 服务名
   * @param timeout 超时，毫秒
   * @return 代理std::shared_ptr<T>
   */
  template <typename T>
  auto get_proxy_co(const std::string &service_name, std::time_t timeout)
      -> std::shared_ptr<T> {
    auto trans = get_transport_co(service_name, timeout);
    if (!trans) {
      return nullptr;
    }
    auto prx = rpc::getService<T>(trans, true, get_rpc());
    if (prx) {
      prx->setRpc(get_rpc());
    }
    return prx;
  }
  /**
   * 根据UUID获取代理，若缓存内未找到则立刻返回
   *
   * \return 代理
   */
  template <typename T> auto try_get_proxy() -> std::shared_ptr<T> {
    auto trans = try_get_transport(std::to_string(T::uuid()));
    if (!trans) {
      return nullptr;
    }
    auto prx = rpc::getService<T>(trans, true, get_rpc());
    if (prx) {
      prx->setRpc(get_rpc());
    }
    return prx;
  }
  /**
   * 根据UUID获取代理.
   *
   * \return 代理
   */
  template <typename T>
  auto get_proxy_sync(std::time_t timeout) -> std::shared_ptr<T> {
    auto trans = get_transport_sync(std::to_string(T::uuid()), timeout);
    if (!trans) {
      return nullptr;
    }
    auto prx = rpc::getService<T>(trans, true, get_rpc());
    if (prx) {
      prx->setRpc(get_rpc());
    }
    return prx;
  }
  /**
   * 根据UUID获取代理，在实例建立过程中将阻塞执行执行 '协程'..
   *
   * \param timeout 超时，毫秒
   * \return 代理
   */
  template <typename T>
  auto get_proxy_co(std::time_t timeout) -> std::shared_ptr<T> {
    auto trans = get_transport_co(std::to_string(T::uuid()), timeout);
    if (!trans) {
      return nullptr;
    }
    auto prx = rpc::getService<T>(trans, true, get_rpc());
    if (prx) {
      prx->setRpc(get_rpc());
    }
    return prx;
  }
  /**
   * 获取服务代理,
   * 需要使用者自己确认对端是否存在这个服务，系统对服务有效性不做保证
   * 通常用于服务器反向调用客户端的服务，但是通过服务发现性能和必要性都有问题，所以
   * 通过这个方法可以高效的将内部服务与外部服务进行发送关联
   *
   * \param stub_call 外部通过proxy发送过来的调用
   * \return 代理std::shared_ptr<T>
   */
  template <typename T>
  auto get_proxy_from_peer(rpc::StubCallPtr stub_call) -> std::shared_ptr<T> {
    if (stub_call->getGlobalIndex() == rpc::INVALID_GLOBAL_INDEX) {
      return nullptr;
    }
    auto prx = rpc::getService<T>(stub_call->getGlobalIndex(),
                                  stub_call->getTransport(), true, get_rpc());
    if (prx) {
      prx->setRpc(get_rpc());
    }
    return prx;
  }
  /**
   * 随意输出的日志, [线程安全]
   *
   * \param log 日志内容
   */
  virtual auto verbose(const std::string &log) -> void = 0;
  /**
   * 信息日志, [线程安全]
   *
   * \param log 日志内容
   */
  virtual auto info(const std::string &log) -> void = 0;
  /**
   * 调试日志, [线程安全]
   *
   * \param log 日志内容
   */
  virtual auto diagnose(const std::string &log) -> void = 0;
  /**
   * 警告日志, [线程安全]
   *
   * \param log 日志内容
   */
  virtual auto warn(const std::string &log) -> void = 0;
  /**
   * 异常日志，捕获了异常, [线程安全]
   *
   * \param log 日志内容
   */
  virtual auto except(const std::string &log) -> void = 0;
  /**
   * 失败日志，错误可以恢复, [线程安全]
   *
   * \param log 日志内容
   */
  virtual auto fail(const std::string &log) -> void = 0;
  /**
   * 不可恢复日志，无法继续运行, [线程安全]
   *
   * \param log 日志内容
   */
  virtual auto fatal(const std::string &log) -> void = 0;
  /**
   * 获取定时调度器
   *
   * \return 定时调度器
   */
  virtual auto new_scheduler() -> SchedulePtr = 0;
  /**
   * 获取HTTP组件
   */
  virtual auto new_http() -> HttpPtr = 0;
  /**
   * 获取内存分配器.
   *
   * \return 内存分配器实例
   */
  virtual auto get_allocator() -> MemoryAllocator & = 0;
  /**
   * 获取命令注册组件
   */
  virtual auto new_command() -> CommandPtr = 0;
  /**
   * 获取Redis组件.
   *
   * \return
   */
  virtual auto new_redis() -> RedisPtr = 0;
  /**
   * 获取统计组件.
   *
   * \return
   */
  virtual auto get_statistics() -> ProcStat * = 0;
  /**
   * 获取模块日志.
   * \param name 模块名称
   * \return
   */
  virtual auto get_module_logger(const std::string &name) -> LoggerPtr = 0;
  /**
   * 获取本地时间.
   *
   * \return
   */
  virtual auto get_local_time() -> LocalTimePtr = 0;
  /**
   * 获取RPC调用统计组件.
   *
   * \return
   */
  virtual auto get_rpc_statistics() -> rpc::StubCallStatistics * = 0;
  /**
   * 获取CSV管理器.
   */
  virtual auto new_csv_manager() -> CsvManagerPtr = 0;
  /**
   * 获取容器控制台
   */
  virtual auto new_box_console(const std::string &name) -> BoxConsolePtr = 0;
  /**
   * 获取LuaService实例 
   */
  virtual auto new_lua_service()-> LuaServicePtr = 0;
  /**
   * @brief 获取工具库接口
   * @return 工具库接口
  */
  virtual auto new_util() -> UtilPtr = 0;

private:
  /**
   * 获取用于RPC通信的传输实例，如果没有则立即返回空实例.
   * @param name 服务名
   * @return 传输实例std::shared_ptr<rpc::Transport>
   */
  virtual auto try_get_transport(const std::string &name)
      -> std::shared_ptr<rpc::Transport> = 0;
  /**
   * 获取用于RPC通信的传输实例，在实例建立过程中将阻塞执行执行线程.
   * @param name 服务名
   * @param timeout 超时，毫秒
   * @return 传输实例std::shared_ptr<rpc::Transport>
   */
  virtual auto get_transport_sync(const std::string &name, std::time_t timeout)
      -> std::shared_ptr<rpc::Transport> = 0;
  /**
   * 获取用于RPC通信的传输实例，在实例建立过程中将阻塞执行执行'协程'.
   * @param name 服务名
   * @param timeout 超时，毫秒
   * @return 传输实例std::shared_ptr<rpc::Transport>
   */
  virtual auto get_transport_co(const std::string &service_name,
                                std::time_t timeout)
      -> std::shared_ptr<rpc::Transport> = 0;
  /**
   * 分配内存
   *
   * \param size 需要分配的长度
   * \return 内存地址
   */
  virtual auto allocate(std::size_t size) -> void * = 0;
  /**
   * 销毁内存
   *
   * \param p 内存地址
   */
  virtual auto deallocate(void *p) -> void = 0;
  /**
   * 获取协程运行器
   */
  virtual auto new_coro_runner() -> std::unique_ptr<CoroRunner> = 0;
  /**
   * 获取代理转发器.
   *
   * \return
   */
  virtual auto get_proxy_handler() -> rpc::ProxyHandler * = 0;
  /**
   * 获取服务容器的默认RPC.
   *
   * \return
   */
  virtual auto get_rpc() -> rpc::Rpc * = 0;
};

} // namespace service
} // namespace kratos
